
# 本程序在sketchz（基于sdl，zengl）之前写成，故使用的是较旧的sketchc（基于pygame，moderngl）


from sketchc import *
import numpy as np


window = Window2((1000, 800))

class Maze(Obj):
    def __init__(self, unit, l, count):
        self.unit = unit
        self.l = np.array(l, dtype="f4")
        self.count = count

        self.mode = moderngl.TRIANGLES
        self.t = Turtle()
        self.box = None

        self.prog =  {
            "vertex_shader":'''
#version 330 core
in vec3 aPos;
in vec3 aNormal;

uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;
//uniform mat4 normalMat;
uniform vec4[100] bbb;

  out vec4 vPos;
  out vec3 wNormal;     // world
  out vec3 vNormal;

void main() {
    vec3 temp = aPos;
    temp.y = temp.y * bbb[gl_InstanceID].w * 2;
    temp.y = max(0, temp.y);
    vPos = modelMat * vec4(temp, 1.0) + vec4(bbb[gl_InstanceID].xyz, 0);
    wNormal = mat3( modelMat) * aNormal;
    vNormal = mat3( viewMat * modelMat) * aNormal;
    //vNormal = mat3(transpose(inverse(modelMat))) * aNormal;
    //vNormal = mat3(normalMat) * aNormal;
    gl_Position = projMat * viewMat * vPos;
  }
            ''',
            "fragment_shader":'''
#version 330 core
out vec4 FragColor;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 objectColor;
uniform int get_normal;

in vec4 vPos;
in vec3 wNormal;
in vec3 vNormal;


void main()
{
    if (get_normal == 3){
        vec3 norm = normalize(vNormal);
        //FragColor = vec4(norm / 2.0 + vec3(0.5, 0.5, 0.5), 1.0);
        FragColor = vec4(norm / 2.0 + vec3(0.5, 0.5, 0.5), gl_FragCoord.z);
        return;
    }


    vec3 FragPos = vPos.xyz;
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * objectColor;

    vec3 norm = normalize(wNormal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir) , 0.0);
    //vec3 diffuse = diff * vec3(0.5, 0.5, 0.5) ;
    vec3 diffuse = diff * objectColor ;
    diffuse = diffuse * 0.7 + 0.3;


    vec3 viewDir = normalize(viewPos - FragPos);
    float spec = 0.0;
    vec3 halfwayDir = normalize(lightDir + viewDir);
    spec = pow(max(dot(wNormal, halfwayDir), 0.0), 32.0);
    vec3 specular = spec * vec3(0.3);

    vec3 result = (ambient + diffuse + specular * 0.1);

    //vec3 red = vec3(1, 0, 0);

    FragColor = vec4(result, 1.0);
    //FragColor = vec4(result * 0 + objectColor, 1.0);
}
            '''}


        self.vao = None


    def __rrshift__(self, obj):
        raise NotImplemented()

    def u(self, ctx):
        assert isinstance(ctx, moderngl.Context)
        self.prog = ctx.program(**self.prog)
        self.vao= self.get_vao(ctx)

    def get_vao(self, ctx):
        vertices = np.array(self.unit.vertices, dtype="f4")
        normals = np.array(self.unit.normals, dtype="f4")
        # ~ print("::", vertices.shape)
        # ~ print("::", normals.shape)
        a, b, c = vertices[:,0], vertices[:,1], vertices[:,2]
        d, e, f = normals[:,0], normals[:,1], normals[:,2]
        x = np.dstack([a, b, c, d, e, f ])
        vbo = ctx.buffer(x.astype("f4"))
        return ctx.simple_vertex_array(self.prog, vbo, 'aPos', 'aNormal')

    def draw(self, camera):
        if self.vao is None:
            print("没准备好绘制", self)
            return


        # ~ lp = U3(100, 0, 200)
        self.prog['projMat'].write(camera.p())
        self.prog['modelMat'].write(self.m())
        self.prog['viewMat'].write(camera.v())
        self.prog['lightPos'].write(camera.pos)
        # ~ self.prog['lightPos'].write(lp)
        self.prog['viewPos'].write(camera.pos + (camera.right() * 10).astype("f4"))
        self.prog['objectColor'].write(V3(0.9, 0.9, 0.9))
        # ~ self.prog['objectColor'].write(V3(140, 165, 213) / 255)
        self.prog['get_normal'].value = 0
        self.prog['bbb'].write(self.l)
        self.vao.render(self.mode, instances=self.count)
        # ~ self.vao.render(self.mode)


    def draw_normal(self, camera):
        if self.vao is None:
            print("没准备好绘制", self)
            return


        lp = U3(50, 0, 50)
        self.prog['modelMat'].write(self.m())
        self.prog['viewMat'].write(camera.v())
        self.prog['projMat'].write(camera.p())
        # ~ self.prog['lightPos'].write(camera.pos)
        self.prog['lightPos'].write(lp)
        self.prog['viewPos'].write(camera.pos)
        self.prog['objectColor'].write(V3(0.5, 0.5, 0.5))

        self.prog['get_normal'].value = 3
        # ~ self.vao.render(self.mode)
        self.prog['bbb'].write(self.l)
        self.vao.render(self.mode, instances=self.count)



# ~ box1 >> window

###随机部分
x, y = np.meshgrid(range(10), range(10))
z = z = np.zeros((10, 10))

if "sin-random" and 0:
    w = np.sin(1 + x * 1.1 + y * 102.3)
    w = np.mod(w * 100, 1)
    w = (w + 0.1) * 3
elif "noise" and 0:
    from noise.perlin import SimplexNoise
    noise = SimplexNoise()
    w = z + 0
    for j in range(10):
        for i in range(10):
            w[i, j] = noise.noise2(i / 10, j / 10)
    w = (w +1) / 2
    w = (w + 0.1) * 5
elif "noise2":
    from noise.perlin import SimplexNoise
    noise = SimplexNoise()
    w = z + 0
    w2 = z + 0
    for j in range(10):
        for i in range(10):
            w[i, j] = noise.noise2(i / 10, j / 10)
            w2[i, j] = noise.noise2(2+i / 10, 2+j / 10)
    w = 10 + w * 5
    w2 = 6 + w2 * 2
    from mapgen import gen, seed
    seed(2)

    xx = gen(5, 2)
    xx = xx[10:20, 10: 20]
    xx2 = xx * (-1) + 1
    print(xx)
    print(xx2)
    w = w * xx + w2 * xx2







p = np.dstack([x, z, -y, w]).reshape((100, 4)).astype("f4")

# ~ p = p * 150
# ~ p1 = p[r < 0.5]
# ~ p2 = p[r > 0.5]
# ~ p11 = np.concatenate((p1, p2))
# ~ p22 = np.concatenate((p2, p1))
###随机部分结束

def madd(l, a):
    return [v + a for v in l]

def with_norm(l):
    return l, easy_norm(l)


def pspace(p1, p2, k):
    dp = p2 - p1
    space = np.linspace(0, 1, k)
    return [p1 + dp * k for k in space]


###################################o2 divide

box1 = make_block(U3(0.9, 0.9, 0.9))
m = Maze(box1, p.astype("f4"), 100)

m.u(window.ctx)
m >> window


camera = Camera(U3(0, -5, 0))
camera >> window        # 识别为camera则自动作为主camera

walker = Walker(camera)
walker >> window
walker.v = 7




clock = pygame.time.Clock()
while True:
    dt = clock.tick(60)
    window.update(dt)
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            exit()
        window.dispatch(e)
    window.render()   # screen 操作需要写在window的槽里
    pygame.display.flip()

